<template>
    <div ref="pieChart" :style="{ height: '100%', width: '100%' }"></div>
</template>

<script>
    import * as echarts from 'echarts'
    import 'echarts-gl'

    export default {
        name: 'PieChart',
        props: {
            tableData: {
                type: Array,
                required: true,
            },
            timeGranularity: {
                type: String,
                default: 'day',
                validator: (value) => ['day', 'month', 'year'].includes(value),
            },
        },
        data() {
            return {
                chart: null,
                hoveredIndex: '',
                autoPlayTimer: null,
                timeType: 'day',
            }
        },
        computed: {
            // 提取计算属性
            pieData() {
                const totalCount = this.tableData.length
                const overBenchmark = this.tableData.filter(
                    (item) => Number(item.benchmarkDiff) >= 0
                ).length

                return [
                    {
                        name: '落后',
                        value: overBenchmark,
                        number: overBenchmark,
                        itemStyle: {
                            opacity: 1,
                            color: '#93DBFF88',
                        },
                    },
                    {
                        name: '对标',
                        value: totalCount - overBenchmark,
                        number: totalCount - overBenchmark,
                        itemStyle: {
                            opacity: 1,
                            color: '#5AF3B888',
                        },
                    },
                ]
            },
        },
        created() {
            this.timeType = this.timeGranularity
        },
        mounted() {
            this.initChart()
            window.addEventListener('resize', this.resizeChart)
        },
        beforeDestroy() {
            this.destroyChart()
            window.removeEventListener('resize', this.resizeChart)
        },
        methods: {
            // 提取公共方法
            updateSeriesItem(series, index, isHovered) {
                const { startRatio, endRatio } = series.pieData
                const { selected, k } = series.pieStatus
                const value = isHovered ? series.pieData.value + 5 : series.pieData.value

                return {
                    ...series,
                    parametricEquation: this.getParametricEquation(
                        startRatio,
                        endRatio,
                        selected,
                        isHovered,
                        k,
                        value
                    ),
                    pieStatus: {
                        ...series.pieStatus,
                        hovered: isHovered,
                    },
                }
            },

            // 提取图表更新方法
            updateChartOption(newSeries) {
                const option = this.chart.getOption()
                this.chart.setOption({
                    ...option,
                    series: newSeries,
                })
            },

            initChart() {
                this.chart = echarts.init(this.$refs.pieChart)
                this.updateChart()

                this.chart.on('mouseover', this.handleMouseover)
                this.chart.on('globalout', this.handleMouseout)
            },

            destroyChart() {
                if (this.chart) {
                    this.chart.dispose()
                    this.chart = null
                }
                if (this.autoPlayTimer) {
                    clearInterval(this.autoPlayTimer)
                    this.autoPlayTimer = null
                }
            },

            updateChart() {
                if (!this.chart) return
                const option = this.getPie3D(this.pieData, 0.71)
                this.chart.setOption(option)
            },

            getParametricEquation(startRatio, endRatio, isSelected, isHovered, k = 1 / 3, h) {
                // 如果只有一个扇形，则不实现选中效果
                if (startRatio === 0 && endRatio === 1) {
                    isSelected = false
                }

                const midRatio = (startRatio + endRatio) / 2
                const startRadian = startRatio * Math.PI * 2
                const endRadian = endRatio * Math.PI * 2
                const midRadian = midRatio * Math.PI * 2

                // 计算选中效果的位移
                const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
                const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0

                // 计算高亮效果的放大比例
                const hoverRate = isHovered ? 1.05 : 1

                const getPosition = (u, radian) => ({
                    x: offsetX + Math.cos(radian) * (1 + Math.cos(u) * k) * hoverRate,
                    y: offsetY + Math.sin(radian) * (1 + Math.cos(u) * k) * hoverRate,
                })

                return {
                    u: {
                        min: -Math.PI,
                        max: Math.PI * 3,
                        step: Math.PI / 32,
                    },
                    v: {
                        min: 0,
                        max: Math.PI * 2,
                        step: Math.PI / 20,
                    },
                    x: function (u, v) {
                        if (u < startRadian) return getPosition(v, startRadian).x
                        if (u > endRadian) return getPosition(v, endRadian).x
                        return getPosition(v, u).x
                    },
                    y: function (u, v) {
                        if (u < startRadian) return getPosition(v, startRadian).y
                        if (u > endRadian) return getPosition(v, endRadian).y
                        return getPosition(v, u).y
                    },
                    z: function (u, v) {
                        if (u < -Math.PI * 0.5) return Math.sin(u)
                        if (u > Math.PI * 2.5) return Math.sin(u) * h * 0.1
                        return Math.sin(v) > 0 ? 1 * h * 0.1 : -1
                    },
                }
            },

            getPie3D(pieData, internalDiameterRatio) {
                const series = []
                let sumValue = 0
                let startValue = 0
                const legendData = []
                const k = internalDiameterRatio
                    ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio)
                    : 1 / 3

                // 添加装饰组件
                series.push(...this.getDecorativeRings())
                series.push(this.getMouseoutSeries())
                // 计算总和
                pieData.forEach((item) => (sumValue += item.value))

                // 生成基础series配置
                pieData.forEach((item) => {
                    const endValue = startValue + item.value
                    const startRatio = startValue / sumValue
                    const endRatio = endValue / sumValue

                    series.push({
                        name: item.name,
                        type: 'surface',
                        parametric: true,
                        wireframe: { show: false },
                        pieData: {
                            ...item,
                            startRatio,
                            endRatio,
                        },
                        pieStatus: {
                            selected: false,
                            hovered: false,
                            k,
                        },
                        itemStyle: item.itemStyle,
                        parametricEquation: this.getParametricEquation(
                            startRatio,
                            endRatio,
                            false,
                            false,
                            k,
                            item.value
                        ),
                    })

                    startValue = endValue
                    legendData.push(item.name)
                })

                return this.getChartOption(legendData, series, sumValue)
            },

            // 提取装饰组件配置
            getMouseoutSeries() {
                return {
                    name: 'mouseoutSeries',
                    type: 'surface',
                    parametric: true,
                    wireframe: { show: false },
                    itemStyle: { opacity: 0 },
                    parametricEquation: {
                        u: { min: 0, max: Math.PI * 2, step: Math.PI / 20 },
                        v: { min: 0, max: Math.PI, step: Math.PI / 20 },
                        x: (u, v) => Math.sin(v) * Math.sin(u) + Math.sin(u),
                        y: (u, v) => Math.sin(v) * Math.cos(u) + Math.cos(u),
                        z: (u, v) => (Math.cos(v) > 0 ? 0.1 : -0.1),
                    },
                }
            },

            getDecorativeRings() {
                const ringConfig = {
                    width: 0.005,
                    size: 1.2,
                    color: '#00619A',
                }

                const getBaseRing = (name, offset, opacity) => ({
                    name,
                    type: 'surface',
                    parametric: true,
                    wireframe: { show: false },
                    itemStyle: {
                        color: ringConfig.color,
                        opacity,
                    },
                    parametricEquation: {
                        u: { min: 0, max: Math.PI * 2, step: Math.PI / 20 },
                        v: { min: 0, max: Math.PI * 2, step: Math.PI / 20 },
                        x: (u, v) =>
                            (ringConfig.size + offset) *
                            Math.cos(u) *
                            (1 + ringConfig.width * Math.cos(v)),
                        y: (u, v) =>
                            (ringConfig.size + offset) *
                            Math.sin(u) *
                            (1 + ringConfig.width * Math.cos(v)),
                        z: (u, v) => ringConfig.width * Math.sin(v),
                    },
                })

                return [
                    getBaseRing('decorativeRing1', 0.2, 0.2),
                    getBaseRing('decorativeRing2', 0.4, 0.1),
                ]
            },

            getChartOption(legendData, series, sumValue) {
                return {
                    legend: {
                        data: legendData,
                        textStyle: { color: '#9AC0EF' },
                        left: 'right',
                    },
                    tooltip: {
                        backgroundColor: 'rgba(0,35,92,0.9)',
                        borderWidth: 1,
                        padding: [10, 15],
                        textStyle: {
                            color: '#fff',
                            fontSize: 14,
                        },
                        extraCssText: `
                            box-shadow: inset 0px 0px 27px 0px rgba(0,156,255,0.3);
                            border-radius: 4px;
                            border: 1px solid transparent;
                            border-image: linear-gradient(360deg, rgba(3, 102, 186, 1), rgba(0, 221, 255, 0.89), rgba(3, 102, 186, 1)) 1;
                        `,
                        formatter: (params) => {
                            if (params.seriesName === 'mouseoutSeries') return

                            const seriesData = series[params.seriesIndex]?.pieData
                            if (!seriesData) return

                            const percentage = ((seriesData.value / sumValue) * 100).toFixed(0)

                            // 根据时间粒度获取对应的单位
                            const timeUnit = {
                                day: '天',
                                month: '月',
                                year: '年',
                            }[this.timeType]

                            return [
                                `类型：${params.seriesName}`,
                                `${timeUnit}数：${seriesData.number}${timeUnit}`,
                                `占比：${percentage}%`,
                            ].join('<br/>')
                        },
                    },
                    xAxis3D: { min: -1, max: 1 },
                    yAxis3D: { min: -1, max: 1 },
                    zAxis3D: { min: -1, max: 1 },
                    grid3D: {
                        show: false,
                        boxHeight: 10,
                        viewControl: {
                            alpha: 35,
                            rotateSensitivity: 0,
                            zoomSensitivity: 0,
                            panSensitivity: 0,
                            autoRotate: false,
                            mouseWheel: false,
                        },
                        postEffect: {
                            enable: true,
                            bloom: {
                                enable: true,
                                bloomIntensity: 0.1,
                            },
                        },
                    },
                    series,
                }
            },

            handleMouseover(params) {
                if (this.hoveredIndex === params.seriesIndex) return

                const option = this.chart.getOption()
                let newSeries = [...option.series]

                // 取消之前的高亮
                if (this.hoveredIndex !== '') {
                    newSeries[this.hoveredIndex] = this.updateSeriesItem(
                        newSeries[this.hoveredIndex],
                        this.hoveredIndex,
                        false
                    )
                }

                // 设置新的高亮
                if (params.seriesName !== 'mouseoutSeries') {
                    newSeries[params.seriesIndex] = this.updateSeriesItem(
                        newSeries[params.seriesIndex],
                        params.seriesIndex,
                        true
                    )
                    this.hoveredIndex = params.seriesIndex
                }

                this.updateChartOption(newSeries)
            },

            handleMouseout() {
                if (this.hoveredIndex === '') return

                const option = this.chart.getOption()
                let newSeries = [...option.series]

                newSeries[this.hoveredIndex] = this.updateSeriesItem(
                    newSeries[this.hoveredIndex],
                    this.hoveredIndex,
                    false
                )

                this.hoveredIndex = ''
                this.updateChartOption(newSeries)
            },

            resizeChart() {
                if (this.chart) {
                    this.chart.resize()
                }
            },
        },
        watch: {
            tableData: {
                handler() {
                    this.updateChart()
                    this.timeType = this.timeGranularity
                },
                deep: true,
            },
        },
    }
</script>
